/*
 * Copyright (C) 2017-2019 Alibaba Group Holding Limited
 */

/******************************************************************************
 * @file     on2_vpu.c
 * @brief    CSI Source File for VPU Driver
 * @version  V1.4
 * @date     26. May 2019
 ******************************************************************************/

#include <stdio.h>
#include <string.h>

//#define VPU_PERF_TRACE
/* LOG_LEVEL: 0: Err; 1: Err&Warn; 2: Err&Warn&Info; 3: Err&Warn&Info&Debug */
#define LOG_LEVEL 0
#include <syslog.h>

#include <soc.h>
#include <drv_irq.h>
#include <drv_vpu.h>
#include <vpu_internal.h>

#include <ewl.h>
#include <enccommon.h>
#include <ewl_tx216_mm.h>
#include "EncJpegCodeFrame.h"
#include <aos/kernel.h>

#include "jpeg_vpu.h"

#include "mm_config.h"
#include "vfs.h"
#include <fcntl.h>

// static u32 pix_byte = 0;

//u8 *outbuf_address;
static JpegEncCfg	cfg;
static JpegEncIn	encIn;
static JpegEncOut	encOut;

int g_jpeg_test = 0;
extern int g_jpeg_Size;
//extern int g_usb_send_flag;
int g_jpeg_commpre_begin = 1;
static vpu_handle_t handle_dev;
//extern uint32_t VPU_OUT_UVC_BASE;

//extern char jpg_out_addr[640*480];
//extern char *jpg_out_addr;

aos_mutex_t vpu_mutex;
aos_sem_t vpu_sem;

static uint32_t g_InOdd = 0;

extern void jpegSetNewFrame(jpegInstance_s *inst);
extern JpegEncRet JpegEncEncodeCfgTest(JpegEncInst inst, const JpegEncIn *pEncIn,JpegEncOut *pEncOut);
extern JpegEncRet JpegEncEncodeStart(JpegEncInst inst, const JpegEncIn *pEncIn, JpegEncOut *pEncOut);
extern JpegEncRet JpegEncEncodeCfg(JpegEncInst inst, const JpegEncIn *pEncIn,JpegEncOut *pEncOut);

static u8 **pJpgBuffer_vpu = NULL;
int mjpg_out_buffer_malloc()
{
	pJpgBuffer_vpu = (u8 **)malloc(1);
	printf("pJpgBuffer_vpu =%p\n", pJpgBuffer_vpu);
	*pJpgBuffer_vpu = (u8 *)malloc(640 * 480);
	if (NULL == pJpgBuffer_vpu[0])
	{
		printf("pJpegBuffer[0]_first_malloc_fail\n");
		return -1;
	}
	printf("pJpgBuffer_vpu[0] =%p\n", pJpgBuffer_vpu[0]);

	//   for(int i=0; i<26;i++)
	//   {
	//   		*(pJpgBuffer_vpu + i) =(u8 *)malloc(640*480);
	//   		if( NULL== pJpgBuffer_vpu[0]){
	//	  		printf("pJpegBuffer malloc_fail\n");
	//	 		return -1;
	//  		 }
	//   		printf("pJpgBuffer_vpu[%d] =%p\n",i,pJpgBuffer_vpu + i);
	//   }

	// *(pJpgBuffer_vpu + 1) = (u8 *)malloc(640 * 480);
	// if (NULL == pJpgBuffer_vpu[1])
	// {
	// 	printf("pJpegBuffer[1]_first_malloc_fail\n");
	// 	return -1;
	// }
	// printf("pJpgBuffer_vpu[1] =%p\n", pJpgBuffer_vpu[1]);
	return 0;
}

JpegEncRet vpu_jpeg_init(u8 *imageDataSource, u8 **pJpegBuffer, int imageWidth, int imageHeight)
{
	JpegEncRet ret;
	u32 outbuf_size = imageWidth * imageHeight;
	u32 pict_bus_address = (u32)imageDataSource;
	u32 *outbuf_virt_address = (u32 *)malloc(outbuf_size);

	//printf("pJpegBuffer=%p\r\n",pJpegBuffer);

	if (NULL == (*pJpegBuffer))
	{
		printf("pJpegBuffer_amlloc_is_=%p\r\n", *pJpegBuffer);
		goto close;
	}
	//printf("pJpegBuffer_amlloc_is_=%p\r\n",*pJpegBuffer);

	cfg.qLevel = 8;
#if 0 //JPEGENC_INPUT_YUV_FORMAT
#if JPEG_YUV422_FORMAT
	  cfg.frameType  = JPEGENC_YUV422_INTERLEAVED_YUYV;
#else
	  cfg.frameType  = JPEGENC_YUV420_PLANAR;  //input_format
#endif
#else
#if JPEG_RGB565_FORMAT
	cfg.frameType = JPEGENC_RGB565;
#else
	cfg.frameType = JPEGENC_RGB888;
#endif
#endif
	cfg.codingType = JPEGENC_WHOLE_FRAME;
	cfg.unitsType = JPEGENC_DOTS_PER_INCH;
	cfg.markerType = JPEGENC_SINGLE_MARKER;
	cfg.xDensity = 72;
	cfg.yDensity = 72;
	cfg.comLength = 0;
	cfg.pCom = NULL;

	/* Step 2: Configuration of picture size */
	cfg.inputWidth = imageWidth;
	cfg.codingWidth = imageWidth;
	cfg.inputHeight = imageHeight;
	cfg.codingHeight = imageHeight;

	cfg.xOffset = 0;
	cfg.yOffset = 0;
	cfg.rotation = JPEGENC_ROTATE_0;
	//cfg.rotation	   = JPEGENC_ROTATE_90L;
	/* Restart interval 5 MCU rows (= 80 pixel rows), average quantization, encode whole frame at once */
	cfg.restartInterval = 5;

	encIn.pOutBuf = (u8 *)outbuf_virt_address;
	encIn.busOutBuf = (u32)(*pJpegBuffer); //(outbuf_address);
	encIn.outBufSize = outbuf_size;		   /* bytes */
	int_input_address = (u32 *)encIn.pOutBuf;
	int_out_address = (u32 *)encIn.busOutBuf;
	//printf("out_address-busOutBuf = %p,input_address--pOutBuf = %p\n",int_out_address,int_input_address);

	encIn.frameHeader = 1;
	switch (cfg.frameType)
	{
	case JPEGENC_YUV422_INTERLEAVED_YUYV:
		encIn.busLum = pict_bus_address;
		break;
	case JPEGENC_YUV420_PLANAR:
		encIn.busLum = pict_bus_address;
		encIn.busCb = encIn.busLum + cfg.inputWidth * cfg.inputHeight;
		encIn.busCr = encIn.busCb + (cfg.inputWidth / 2) * (cfg.inputHeight / 2);
		break;
	case JPEGENC_RGB565:
		encIn.busLum = pict_bus_address;
		break;
	case JPEGENC_RGB888:
		encIn.busLum = pict_bus_address;
		break;
	default:
		printf("input_image_format  error_no=%d\n", cfg.frameType);
		return -1;
	}

	if ((ret = JpegEncInit(&cfg, &encoder)) != JPEGENC_OK)
	{
		/* Handle here the error situation */
		printf("JpegEncInit() failed. error_no=%d\n", ret);
		//	return -1;
		goto close;
	}

	if ((ret = JpegEncSetPictureSize(encoder, &cfg)) != JPEGENC_OK)
	{
		/* Handle here the error situation */
		printf("JpegEncSetPictureSize() failed. error_no=%d\n", ret);
		//	return -1;
		goto close;
	}
	return JPEGENC_OK;

close:
	if (NULL != outbuf_virt_address)
	{
		free(outbuf_virt_address);
		encIn.pOutBuf = NULL;
		int_input_address = NULL;
	}
	return JPEGENC_ERROR;
}

JpegEncRet vpu_jpeg_start(u32 *OutSize)
{
	JpegEncRet ret;

	ret = JpegEncEncodeCfg(encoder, &encIn, &encOut);
	if (ret != JPEGENC_OK)
	{
		printf("JpegEncEncodeCfg() failed. error_no=%d\n", ret);
		return ret;
	}
	ret = JpegEncEncodeStart(encoder, &encIn, &encOut);
	if (ret != JPEGENC_OK)
	{
		printf("JpegEncEncodeStart() failed. error_no=%d\n", ret);
		return ret;
	}

	//jpg_out_addr = (u8*)encIn.busOutBuf;
	*OutSize = (u32)g_jpeg_Size;
	//     g_InOdd ^= 1;
	g_InOdd++;
	//memcpy(jpg_out_addr, (u8 *)encIn.busOutBuf, g_jpeg_Size);
	// memcpy(0x6e00000,(u8 *)encIn.busOutBuf,g_jpeg_Size);
	printf("encIn.busOutBuf = %p, g_jpeg_Size = %d\r\n", (u8 *)encIn.busOutBuf, g_jpeg_Size);
	//     encIn.busOutBuf = g_InOdd ? pJpgBuffer_vpu[1]:pJpgBuffer_vpu[0];
	// encIn.busOutBuf = pJpgBuffer_vpu[0];
	//     encIn.busOutBuf = pJpgBuffer_vpu[0] + g_jpeg_Size;
	//encIn.busOutBuf = pJpgBuffer_vpu[g_InOdd];

	return ret;
}

JpegEncRet vpu_jpeg_end(void)
{
	JpegEncRet ret;

	if ((ret = JpegEncRelease(encoder)) != JPEGENC_OK)
	{
		printf("JpegEncRelease() failed. error_no=%d\n", ret);
		return ret;
	}
	return ret;
}

JpegEncRet test_jpeg(u8 *imageDataSource,
					 u8 **pJpegBuffer,
					 int imageWidth,
					 int imageHeight,
					 int channel,
					 u32 *OutSize)
{

	JpegEncRet ret;
	if ((ret = vpu_jpeg_init(imageDataSource, pJpegBuffer, imageWidth, imageHeight)) != JPEGENC_OK)
	{
		printf("vpu_jpeg_init() failed. error_no=%d\n", ret);
		return ret;
	}

	if ((ret = vpu_jpeg_start(OutSize)) != JPEGENC_OK)
	{
		printf("vpu_jpeg_start() failed. error_no=%d\n", ret);
		return ret;
	}

	if ((ret = vpu_jpeg_end()) != JPEGENC_OK)
	{
		printf("vpu_jpeg_end() failed. error_no=%d\n", ret);
		return ret;
	}

	return JPEGENC_OK;
}
/*******************************************************************************	 
Function: 	  vpu_jpg_test
Description:  Simple jpg compression test API
Others:		  just focus on the while 
 *******************************************************************************/
void jpeg_vpu()
{
	JpegEncRet ret;

	//u8 *pJpgBuffer = (u8 *)malloc(640*480/4);//0x6b00000;//

	u32 OutJpegSize;
	// u32 length;

	drv_vpu_clk_enable(ENABLE);
#if 0
	u8 *pJpgBuffer = (u8 *)malloc(640*480);//0x6b00000;//
	if(pJpgBuffer == NULL)
	{
		printf("uvc_tx_pJpgBuffer failed. \n");
		return -1;
	}
	else{
		printf("**********pJpgBuffer = %p***********\n",pJpgBuffer);
	}
#endif
	mjpg_out_buffer_malloc();

	handle_dev = csi_vpu_initialize(0, NULL, NULL);
	if (NULL == handle_dev)
	{
		printf("csi_vpu_initialize() failed. \n");
		goto err_hander;
	}
	//VPU_OUT_UVC_BASE
	if ((ret = vpu_jpeg_init((u8 *)MM_IMAGE_DPU_BUF0, pJpgBuffer_vpu, 480, 640)) != JPEGENC_OK)
	{
		printf("vpu_jpeg_init() failed. error_no=%d\n", ret);
		return;
	}
	g_jpeg_commpre_begin = 0;
	/****Compression begins*****/
	if ((ret = vpu_jpeg_start(&OutJpegSize)) != JPEGENC_OK)
	{
		printf("vpu_jpeg_start() failed. error_no=%d\n", ret);
		return;
	}
	int fd = aos_open("/sdcard/jpeg.jpeg", O_CREAT | O_WRONLY);
	if (fd < 0)
	{
		printf("open jpef file failed.\r\n");
	}
	ret = aos_lseek(fd, 0, SEEK_SET);
	if (ret < 0)
	{
		printf("jpeg file lseek failed.\r\n");
		aos_close(fd);
		return;
	}
	ret = aos_write(fd, (const void *)encIn.busOutBuf, g_jpeg_Size);
	if (ret != g_jpeg_Size)
	{
		printf("write jped file failed,\r\n");
		aos_close(fd);
		return;
	}
	aos_close(fd);
	if ((ret = vpu_jpeg_end()) != JPEGENC_OK)
	{
		printf("vpu_jpeg_end() failed. error_no=%d\n", ret);
		return;
	}
	csi_vpu_uninitialize(handle_dev);
	return;

err_hander:
	csi_vpu_uninitialize(handle_dev);
	return;
}
